import ipaddress
from intervaltree import Interval, IntervalTree
import pandas as pd
from pyecharts import options as opts
from pyecharts.charts import Bar, Line, Pie


# 读取Excel文件
file_path = "accesslog.xlsx"
ip_data = []
with open('ip.txt', 'r', encoding='utf-8') as file:
    for line in file:
        # 分割前三列
        parts = line.strip().split(None, 3)
        # 确保每行数据有四个元素
        if len(parts) == 4:
            ip_data.append(parts)
df = pd.read_excel(file_path)
ip_data = pd.DataFrame(ip_data, columns=['start_ip', 'end_ip', 'location', 'ISP'])
# 1统计独立IP
unique_ips = df['ip'].nunique()
bar = (
    Bar()
    .add_xaxis(['ip'])
    .add_yaxis("访问次数", [unique_ips])
    .set_global_opts(title_opts=opts.TitleOpts(title="独立IP访问次数统计"))
)
# 生成HTML文件
bar.render("ip统计.html")
# 2统计访问趋势
# 将字符串格式的访问时间转换为Datetime对象
df['access_time'] = pd.to_datetime(df['access_time'].str.strip('[]'), format='%d/%b/%Y:%H:%M:%S')
# 按照访问时间统计人次
access_trend = df.groupby(df['access_time'].dt.strftime('%Y-%m-%d %H')).size().reset_index(name='count')
# 绘制折线图
line = (
    Line()
    .add_xaxis(access_trend['access_time'].tolist())
    .add_yaxis("访问人次", access_trend['count'].tolist(),
               markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max")]))
    .set_global_opts(title_opts=opts.TitleOpts(title="访问趋势统计"),
                     xaxis_opts=opts.AxisOpts(axislabel_opts={"rotate": 45}),
                     datazoom_opts=[opts.DataZoomOpts(range_start=0, range_end=100)])
)
line.render("访问趋势.html")
# 3统计访问来源
df['source_type'] = df['referrer'].apply(
    lambda x: '直接访问' if pd.isnull(x) else ('搜索引擎' if 'www.baidu.com' in x else '其他链接'))
# 统计访问来源类型
source_counts = df['source_type'].value_counts().reset_index()
source_counts.columns = ['source_type', 'count']
# 计算占比
total_visits = len(df)
source_counts['percentage'] = source_counts['count'] / total_visits * 100
# 绘制饼图
pie = (
    Pie()
    .add(
        series_name="访问来源",
        data_pair=[(row['source_type'], row['count']) for _, row in source_counts.iterrows()],
        radius=["30%", "70%"],
        label_opts=opts.LabelOpts(is_show=True, position="inside", font_size=12, formatter="{b}: {d}%"),
    )
    .set_global_opts(title_opts=opts.TitleOpts(title="访问来源统计"))
)
# 生成HTML文件
pie.render("访问来源.html")
# 5  统计访问链接Top 10
top_urls = df['url'].value_counts().nlargest(10).reset_index()
top_urls.columns = ['url', 'count']
# 绘制饼图
pie = (
    Pie()
    .add(
        series_name="访问链接TOP10",
        data_pair=[(row['url'], row['count']) for _, row in top_urls.iterrows()],
        radius=["30%", "70%"],
        label_opts=opts.LabelOpts(is_show=True, position="inside", font_size=12, formatter="{b}: {d}%"),
    )
    .set_global_opts(title_opts=opts.TitleOpts(title="用户访问链接TOP10统计"))
)
# 生成HTML文件
pie.render("用户访问链接TOP10.html")
# 6统计恶意攻击IP
# 筛选出响应码为400-404的访问请求
malicious_ips = df[(df['status'] >= 400) & (df['status'] <= 404)]['ip'].value_counts().reset_index()
malicious_ips.columns = ['ip', 'attack_count']
# 仅保留攻击次数大于等于10的记录
malicious_ips = malicious_ips[malicious_ips['attack_count'] >= 10]
# 绘制柱状图
bar = (
    Bar()
    .add_xaxis(malicious_ips['ip'].tolist())
    .add_yaxis("攻击次数", malicious_ips['attack_count'].tolist(),
               markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max")]))
    .set_global_opts(title_opts=opts.TitleOpts(title="恶意攻击IP统计"),
                     xaxis_opts=opts.AxisOpts(axislabel_opts={"rotate": 45}),
                     datazoom_opts=[opts.DataZoomOpts(range_start=0, range_end=100)])
)
# 生成HTML文件
bar.render("恶意攻击IP.html")


# 8统计访客浏览器
# 解析traffic列获取浏览器信息
def get_browser_info(user_agent):
    ua = parse(user_agent)
    return ua.browser.family


df['browser'] = df['traffic'].apply(get_browser_info)
# 统计浏览器信息
browser_count = df['browser'].value_counts()
# 获取浏览器名称和对应的访问次数
browser_names = browser_count.index.tolist()
browser_values = browser_count.values.tolist()
# 制作饼图
pie = (
    Pie()
    .add(
        "",
        [list(z) for z in zip(browser_names, browser_values)],
        radius=["30%", "75%"],
        center=["50%", "50%"],
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="访客浏览器统计"),
        legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
    )
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
)
pie.render("浏览器统计.html")


# 9操作系统
# 解析traffic列获取操作系统信息
def get_os_info(user_agent):
    ua = parse(user_agent)
    return ua.os.family


df['os'] = df['traffic'].apply(get_os_info)
# 统计操作系统信息
os_count = df['os'].value_counts()
# 获取操作系统名称和对应的访问次数
os_names = os_count.index.tolist()
os_values = os_count.values.tolist()
# 制作饼图
pie = (
    Pie()
    .add(
        "",
        [list(z) for z in zip(os_names, os_values)],
        radius=["30%", "75%"],
        center=["50%", "50%"],
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="访客操作系统统计"),
        legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
    )
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
)
# 保存为 HTML 文件或直接显示
pie.render("操作系统.html")


# 10.运营商
# 创建区间 树
ip_tree = IntervalTree()
# 遍历数据框，添加区间到树中
for _, row in ip_data.iterrows():
    if _ == 0:
        continue
    start_ip_int = int(ipaddress.ip_address(row['start_ip']))
    end_ip_int = int(ipaddress.ip_address(row['end_ip']))
    ip_tree[start_ip_int:end_ip_int + 1] = row['ISP']


def find_ip_location(ip_address):
    try:
        ip_int = int(ipaddress.ip_address(ip_address))
        results = ip_tree[ip_int]
        if results:
            isp = next(iter(results)).data
            return isp if isp in ['移动', '联通', '电信'] else '其它'
        else:
            return "其它"
    except ValueError:
        return "其它"


df['ISP'] = df['ip'].apply(find_ip_location)
isp_counts = df['ISP'].value_counts()
# 创建饼状图
pie_chart = Pie()
pie_chart.add("", [list(z) for z in zip(isp_counts.index.tolist(), isp_counts.tolist())])
# 设置全局选项
pie_chart.set_global_opts(
    title_opts=opts.TitleOpts(title="ISP分布"),
    legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%")
)
# 设置系列选项
pie_chart.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {d}%"))
# 生成图表的HTML文件
pie_chart.render('运营商.html')
